//===--- Stencil.h - Stencil class ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines the *Stencil* abstraction: a code-generating object,
/// parameterized by named references to (bound) AST nodes.  Given a match
/// result, a stencil can be evaluated to a string of source code.
///
/// A stencil is similar in spirit to a format string: it is composed of a
/// series of raw text strings, references to nodes (the parameters) and helper
/// code-generation operations.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_
#define LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Transformer/MatchConsumer.h"
#include "clang/Tooling/Transformer/RangeSelector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <string>
#include <vector>

namespace clang {
namespace transformer {
/// A stencil is represented as a sequence of "parts" that can each individually
/// generate a code string based on a match result.  The different kinds of
/// parts include (raw) text, references to bound nodes and assorted operations
/// on bound nodes.
///
/// Users can create custom Stencil operations by implementing this interface.
class StencilPartInterface {
public:
  virtual ~StencilPartInterface() = default;

  /// Evaluates this part to a string and appends it to \c Result.  \c Result is
  /// undefined in the case of an error.
  virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
                           std::string *Result) const = 0;

  /// Constructs a string representation of the StencilPart. StencilParts
  /// generated by the `selection` and `run` functions do not have a unique
  /// string representation.
  virtual std::string toString() const = 0;

protected:
  StencilPartInterface() = default;

  // Since this is an abstract class, copying/assigning only make sense for
  // derived classes implementing `clone()`.
  StencilPartInterface(const StencilPartInterface &) = default;
  StencilPartInterface &operator=(const StencilPartInterface &) = default;
};

/// A copyable facade for a std::unique_ptr<StencilPartInterface>. Copies result
/// in a copy of the underlying pointee object.
class StencilPart {
public:
  explicit StencilPart(std::shared_ptr<StencilPartInterface> Impl)
      : Impl(std::move(Impl)) {}

  /// See `StencilPartInterface::eval()`.
  llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
                   std::string *Result) const {
    return Impl->eval(Match, Result);
  }

  std::string toString() const {
    if (Impl == nullptr)
      return "";
    return Impl->toString();
  }

private:
  std::shared_ptr<StencilPartInterface> Impl;
};

/// A sequence of code fragments, references to parameters and code-generation
/// operations that together can be evaluated to (a fragment of) source code,
/// given a match result.
class Stencil {
public:
  Stencil() = default;

  /// Composes a stencil from a series of parts.
  template <typename... Ts> static Stencil cat(Ts &&... Parts) {
    Stencil S;
    S.Parts = {wrap(std::forward<Ts>(Parts))...};
    return S;
  }

  /// Appends data from a \p OtherStencil to this stencil.
  void append(Stencil OtherStencil);

  // Evaluates the stencil given a match result. Requires that the nodes in the
  // result includes any ids referenced in the stencil. References to missing
  // nodes will result in an invalid_argument error.
  llvm::Expected<std::string>
  eval(const ast_matchers::MatchFinder::MatchResult &Match) const;

  // Allow Stencils to operate as std::function, for compatibility with
  // Transformer's TextGenerator.
  llvm::Expected<std::string>
  operator()(const ast_matchers::MatchFinder::MatchResult &Result) const {
    return eval(Result);
  }

  /// Constructs a string representation of the Stencil. The string is not
  /// guaranteed to be unique.
  std::string toString() const {
    std::vector<std::string> PartStrings;
    PartStrings.reserve(Parts.size());
    for (const auto &Part : Parts)
      PartStrings.push_back(Part.toString());
    return llvm::join(PartStrings, ", ");
  }

private:
  static StencilPart wrap(llvm::StringRef Text);
  static StencilPart wrap(RangeSelector Selector);
  static StencilPart wrap(StencilPart Part) { return Part; }

  std::vector<StencilPart> Parts;
};

//
// Functions for conveniently building stencils.
//

/// Convenience wrapper for Stencil::cat that can be imported with a using decl.
template <typename... Ts> Stencil cat(Ts &&... Parts) {
  return Stencil::cat(std::forward<Ts>(Parts)...);
}

/// \returns exactly the text provided.
StencilPart text(llvm::StringRef Text);

/// \returns the source corresponding to the selected range.
StencilPart selection(RangeSelector Selector);

/// Generates the source of the expression bound to \p Id, wrapping it in
/// parentheses if it may parse differently depending on context. For example, a
/// binary operation is always wrapped, while a variable reference is never
/// wrapped.
StencilPart expression(llvm::StringRef Id);

/// Constructs an idiomatic dereferencing of the expression bound to \p ExprId.
/// \p ExprId is wrapped in parentheses, if needed.
StencilPart deref(llvm::StringRef ExprId);

/// Constructs an expression that idiomatically takes the address of the
/// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if
/// needed.
StencilPart addressOf(llvm::StringRef ExprId);

/// Constructs a `MemberExpr` that accesses the named member (\p Member) of the
/// object bound to \p BaseId. The access is constructed idiomatically: if \p
/// BaseId is bound to `e` and \p Member identifies member `m`, then returns
/// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise.
/// Additionally, `e` is wrapped in parentheses, if needed.
StencilPart access(llvm::StringRef BaseId, StencilPart Member);
inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) {
  return access(BaseId, text(Member));
}

/// Chooses between the two stencil parts, based on whether \p ID is bound in
/// the match.
StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart,
                    StencilPart FalsePart);

/// Chooses between the two strings, based on whether \p ID is bound in the
/// match.
inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText,
                           llvm::StringRef FalseText) {
  return ifBound(Id, text(TrueText), text(FalseText));
}

/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil.
/// This supports user-defined extensions to the Stencil language.
StencilPart run(MatchConsumer<std::string> C);

/// For debug use only; semantics are not guaranteed.
///
/// \returns the string resulting from calling the node's print() method.
StencilPart dPrint(llvm::StringRef Id);
} // namespace transformer

namespace tooling {
// DEPRECATED: These are temporary aliases supporting client migration to the
// `transformer` namespace.
using Stencil = transformer::Stencil;
using StencilPart = transformer::StencilPart;
namespace stencil {
using transformer::access;
using transformer::addressOf;
using transformer::cat;
using transformer::deref;
using transformer::dPrint;
using transformer::expression;
using transformer::ifBound;
using transformer::run;
using transformer::selection;
using transformer::text;
/// \returns the source corresponding to the identified node.
/// FIXME: Deprecated. Write `selection(node(Id))` instead.
inline transformer::StencilPart node(llvm::StringRef Id) {
  return selection(tooling::node(Id));
}
} // namespace stencil
} // namespace tooling
} // namespace clang
#endif // LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_
